using System;
using System.Collections.Generic;
using System.Text;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Dynamics.Springs;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Factories;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Net;

namespace GrapplingHookGameData
{
    public class PlayerOLD : PhysicsObject
    {
        #region Fields
        protected GraphicsDevice graphicsDevice;

        static LineBrush lineBrush = new LineBrush(1, Color.Black);

        public bool playerStanding = false;
        public float playerDirection = 1.0f;

        public int health = Constants.PLAYER_HEALTH;

        //store grenades player have. There is a maximum number to premvent too many grenades on screen, as well as not needing to instance new grenades all the time
        private List<Grenade> grenades = new List<Grenade>(Constants.PLAYER_MAX_GRENADES);
    

        //rope
        SpringRectangleRope rope;
        LinearSpring ropeAttach;
        FixedLinearSpring ropeWorldAttach;
        Texture2D anchorTexture;
        Body anchorBody;
        Geom anchorGeom;
        Vector2 anchorOrigin;


        RopeStates ropeState = RopeStates.NONE;
        enum RopeStates
        {
            NONE, EXTENDING, CONNECTED
        }

        public PlayerMoveActions nextMoveAction = PlayerMoveActions.NONE;
        public enum PlayerMoveActions
        {
            NONE, RIGHT, LEFT, FIRE, FIRE_ROPE, DISCONNECT_ROPE
        }

        public PlayerRopeActions nextRopeAction = PlayerRopeActions.NONE;
        public enum PlayerRopeActions
        {
            NONE, FIRE_ROPE, DISCONNECT_ROPE
        }

        public PlayerWeaponActions nextGunAction = PlayerWeaponActions.NONE;
        public enum PlayerWeaponActions
        {
            NONE, GRENADE
        }
        #endregion

        #region Initialization

        public PlayerOLD(Vector2 startPosition)
            : base(startPosition, Constants.PLAYER_WIDTH, Constants.PLAYER_HEIGHT, "player", false)
        {
            mass = 1;
            //create all grenades
            int i = Constants.PLAYER_MAX_GRENADES;
            while(  i-- > 0)
            {
                grenades.Add(new Grenade(Vector2.Zero, this));
            }
        }



        public void LoadContent(GraphicsDevice graphicsDevice, ContentManager content, PhysicsSimulator physicsSimulator)
        {
            base.LoadContent(content, physicsSimulator);
            lineBrush.Load(graphicsDevice);
            this.graphicsDevice = graphicsDevice;


            body = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, width, height, mass);
            body.Position = position;
            body.MomentOfInertia = float.PositiveInfinity;
            body.LinearDragCoefficient = 1;
            geom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, body, width, height);
            Geom playerFeetGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, body, width, 5, new Vector2(0, height / 2f + 2.5f), 0);
            playerFeetGeom.CollisionResponseEnabled = true;
            geom.CollisionGroup = 1;
            geom.FrictionCoefficient = 0.8f;
            geom.Tag = this;
            playerFeetGeom.CollisionGroup = 1;
            playerFeetGeom.OnCollision += new Geom.CollisionEventHandler(onCollision);


            //anchor
            anchorTexture = DrawingHelper.CreateCircleTexture(graphicsDevice, 10, Color.Black);
            anchorOrigin = new Vector2(anchorTexture.Width / 2, anchorTexture.Height / 2);
            anchorBody = BodyFactory.Instance.CreateCircleBody(physicsSimulator, 10, 0.3f);
            anchorGeom = GeomFactory.Instance.CreateCircleGeom(physicsSimulator, anchorBody, 10, 8);
            anchorGeom.CollisionGroup = 1;
            anchorGeom.OnCollision += new Geom.CollisionEventHandler(onRopeAnchorCollision);
            anchorBody.Enabled = false;

            //load grenades
            foreach (Grenade g in grenades)
            {
                g.LoadContent(content, physicsSimulator);
            }

        }
        #endregion

        #region Update
        /// <summary>
        /// Update physics forces on player depending on next requested action.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime, bool isHost)
        {
            base.Update(gameTime, isHost);

            if (ropeState == RopeStates.EXTENDING && ((anchorBody.Position - Body.Position).Length() > Constants.ROPE_MAX_LENGTH))
            {
                DisconnectRope();
            }

            int force = 100;
            if (playerStanding) force = 700;
            switch (nextMoveAction)
            {
                case PlayerMoveActions.RIGHT:
                    Body.ApplyForce(new Vector2(force, 0));
                    playerDirection = 1.0f;
                    break;
                case PlayerMoveActions.LEFT:
                    Body.ApplyForce(new Vector2(-force, 0));
                    playerDirection = -1.0f;
                    break;
            }
            nextMoveAction = PlayerMoveActions.NONE;

            switch (nextRopeAction)
            {
                case PlayerRopeActions.DISCONNECT_ROPE:
                    DisconnectRope();
                    break;
                case PlayerRopeActions.FIRE_ROPE:
                    FireAnchor();
                    break;
            }
            nextRopeAction = PlayerRopeActions.NONE;

            switch (nextGunAction)
            {
                case PlayerWeaponActions.GRENADE:
                    ThrowGrenade();
                    break;
            }
            nextGunAction = Player.PlayerWeaponActions.NONE;

            //update grenades
            foreach (Grenade g in grenades)
            {
                g.Update(gameTime, isHost);
            }

        }

        /// <summary>
        /// Called when player feet touches something
        /// </summary>
        /// <param name="geometry1"></param>
        /// <param name="geometry2"></param>
        /// <param name="contactList"></param>
        /// <returns></returns>
        public bool onCollision(Geom geometry1, Geom geometry2, ContactList contactList)
        {
            if (geometry2.Body.IsStatic) playerStanding = true;
            return false;
        }

        public bool onRopeAnchorCollision(Geom geometry1, Geom geometry2, ContactList contactList)
        {
            if (geometry2.Body.IsStatic)
            {
                anchorBody.Enabled = false; //stop anchor
                ConnectRope();
            }
            return false;
        }
        #endregion

        #region Actions

        public void ThrowGrenade()
        {
            //see if grenades are available to throw
            Grenade availableGrenade = null;
            foreach (Grenade g in grenades)
            {
                if (g.CurrentState == Grenade.GrenadeState.INACTIVE)
                {
                    availableGrenade = g;
                    break;
                }
            }

            if (availableGrenade != null)
            {
                availableGrenade.Position = body.Position + new Vector2(playerDirection * Constants.PLAYER_WIDTH, -Constants.PLAYER_HEIGHT / 2);
                availableGrenade.Body.ApplyImpulse(new Vector2(playerDirection * Constants.THROW_IMPULSE, -Math.Abs(playerDirection * Constants.THROW_IMPULSE)));
                availableGrenade.Body.ApplyAngularImpulse(100.0f * playerDirection);
                availableGrenade.Activate();
            }
        }

        public void FireAnchor()
        {
            DisconnectRope();
            ropeState = RopeStates.EXTENDING;
            anchorBody.ResetDynamics();
            anchorBody.ApplyImpulse(new Vector2(80 * playerDirection, -80));
            anchorBody.Position = Body.Position + new Vector2(0, -height / 2f);
            anchorBody.Enabled = true;
        }

        private void ConnectRope()
        {
            ropeState = RopeStates.CONNECTED;
            rope = new SpringRectangleRope();

            rope.Position = anchorBody.Position;
            rope.RectangleCount = (int)Math.Round((anchorBody.Position - Body.Position).Length() / Constants.ROPE_JOINT_DISTANCE);

            rope.Load(graphicsDevice, physicsSimulator, playerDirection);

            ropeWorldAttach = ControllerFactory.Instance.CreateFixedLinearSpring(physicsSimulator, rope.FirstBody, Vector2.Zero, anchorBody.Position, 200, 4);
            ropeAttach = ControllerFactory.Instance.CreateLinearSpring(physicsSimulator, rope.LastBody, Vector2.Zero, Body, new Vector2(0, -height / 2f), 200, 4);
            ropeAttach.RestLength = 10;
            //            ropeAttach.Enabled = false;

        }

        public void DisconnectRope()
        {
            anchorBody.Enabled = false;
            if (ropeAttach != null)
            {
                ropeAttach.Enabled = false;
                physicsSimulator.Remove(ropeAttach);
            }
            if (ropeWorldAttach != null)
            {
                ropeWorldAttach.Enabled = false;
                physicsSimulator.Remove(ropeWorldAttach);
            }
            if (rope != null) rope.Remove(physicsSimulator);
            rope = null;
            ropeState = RopeStates.NONE;
        }
        #endregion

        #region Draw
        public override void Draw(SpriteBatch spriteBatch)
        {
            base.Draw(spriteBatch);

            if (ropeState == RopeStates.EXTENDING)
            {
                spriteBatch.Draw(anchorTexture, anchorBody.Position, null, Color.White, anchorBody.Rotation, anchorOrigin, 1, SpriteEffects.None, 0f);
                lineBrush.Draw(spriteBatch, Body.Position - new Vector2(0, height / 2f), anchorBody.Position);
            }
            else if (ropeState == RopeStates.CONNECTED)
            {
                spriteBatch.Draw(anchorTexture, anchorBody.Position, null, Color.White, anchorBody.Rotation, anchorOrigin, 1, SpriteEffects.None, 0f);
                rope.Draw(spriteBatch);
            }

            //draw grenades
            foreach (Grenade g in grenades)
            {
                g.Draw(spriteBatch);
            }
        }
        #endregion

        #region Networking
        /// <summary>
        /// Writes the player data that is needed by clients to draw this player and related objects
        /// </summary>
        /// <param name="packetWriter"></param>
        public void WriteDataToClients(PacketWriter packetWriter)
        {
            packetWriter.Write(Position);
            packetWriter.Write((int)ropeState);
            packetWriter.Write(anchorBody.Position);
            packetWriter.Write(health);

            foreach (Grenade g in grenades)
            {
                g.WriteDataToClients(packetWriter);
            }
        }

        /// <summary>
        /// Read the player data that is needed by clients to draw this player
        /// </summary>
        /// <param name="packetWriter"></param>
        public void ReadDataFromHost(PacketReader packetReader)
        {
            //clients set the position directly
            Position = packetReader.ReadVector2();
            ropeState = (RopeStates)packetReader.ReadInt32();
            anchorBody.Position = packetReader.ReadVector2();
            health = packetReader.ReadInt32();

            //TODO: for now clients never draw the connected rope since sending of rope information is not implamented yet
            if (ropeState == RopeStates.CONNECTED) ropeState = RopeStates.EXTENDING;

            foreach (Grenade g in grenades)
            {
                g.ReadDataFromHost(packetReader);
            }

        }

        /// <summary>
        /// Writes the player data that is needed by the host. This is the actions that the player wishes to take.
        /// </summary>
        /// <param name="packetWriter"></param>
        public void WriteDataToHost(PacketWriter packetWriter)
        {
            packetWriter.Write((int)nextMoveAction);
            packetWriter.Write((int)nextRopeAction);
            packetWriter.Write((int)nextGunAction);
            nextMoveAction = Player.PlayerMoveActions.NONE;
            nextRopeAction = Player.PlayerRopeActions.NONE;
            nextGunAction = Player.PlayerWeaponActions.NONE;
        }

        /// <summary>
        /// Read the player data that is needed by host to update the player. This is the actions that the player wishes to take.
        /// </summary>
        /// <param name="packetWriter"></param>
        public void ReadDataFromClient(PacketReader packetReader)
        {
            nextMoveAction = (Player.PlayerMoveActions)packetReader.ReadInt32();
            nextRopeAction = (Player.PlayerRopeActions)packetReader.ReadInt32();
            nextGunAction = (Player.PlayerWeaponActions)packetReader.ReadInt32();
        }
        #endregion
    }
}
